package com.cloud.utils;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.cloud.common.constants.DataTableFields;
import com.cloud.common.domain.Result;
import com.cloud.controller.BaseController;
import com.cloud.domain.BaseEntity;
import com.cloud.enums.BusinessType;
import com.cloud.logs.Logs;
import com.cloud.security.aop.RequestPermissions;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

public class GeneratorUtils {

    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入:" + tip + ":");
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    /**
     * 接口构造生成器
     *
     * @param jdbcUrl          数据源地址
     * @param driverName       数据源驱动名称
     * @param userName         数据源账户
     * @param passWord         数据源密码
     * @param serveModuleName  所在微服务地址
     * @param servePackageName 微服务项目包路径
     */
    public static void apiGenerator(String jdbcUrl, String driverName, String userName,
                                    String passWord, String serveModuleName, String servePackageName) {
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "\\" + serveModuleName;
        gc.setOutputDir(projectPath + "\\src\\main\\java");
        gc.setAuthor(scanner("开发者名称"));
        // 是否覆盖已有文件
        gc.setFileOverride(false);
        // 开启ActiveRecord模式
        gc.setActiveRecord(false);
        // 开启BaseResultMap
        gc.setBaseResultMap(true);
        // xml二级缓存
        gc.setEnableCache(false);
        // 开启BaseColumnList
        gc.setBaseColumnList(true);
        // 时间类型对应策略
        gc.setDateType(DateType.ONLY_DATE);
        // 实体属性 Swagger2 注解
        gc.setSwagger2(true);
        // 生成完毕后是否打开输出目录
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(jdbcUrl);
        dsc.setDriverName(driverName);
        dsc.setUsername(userName);
        dsc.setPassword(passWord);
        mpg.setDataSource(dsc);
        // 包名配置
        String serveName = scanner("服务模块包名称(只写[com.cloud]之后的包名称)");
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(serveName);
        pc.setParent(servePackageName);
        mpg.setPackageInfo(pc);
        String simpleName = scanner("服务网关转发名称");
        String moduleName = scanner("定义Controller文件@RequestMapping名称");
        String customPort = scanner("是/否生成[增删改查?](Y/N 输入字母必须大写)");
        String customTable = scanner("是/否生成[导入/导出?](Y/N 输入字母必须大写)");
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("serveName", serveName);
                map.put("simpleName", simpleName);
                map.put("moduleName", moduleName);
                map.put("customPort", customPort);
                map.put("customTable", customTable);
                map.put("permissions", RequestPermissions.class.getCanonicalName());
                map.put("businessType", BusinessType.class.getCanonicalName());
                map.put("customLogs", Logs.class.getCanonicalName());
                map.put("customResult", Result.class.getCanonicalName());
                map.put("baseController", BaseController.class.getCanonicalName());
                this.setMap(map);
            }
        };
        // 模板引擎 freemarker
        String mapperPath = "\\templates\\xml\\mapper.xml.ftl";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(mapperPath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名,如果你Entity设置了前后缀、此处注意 xml 的名称会跟着发生变化！
                return projectPath + "\\src\\main\\resources\\mapper\\" + pc.getModuleName()
                        + "\\" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置自定义输出模板
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setController("\\templates\\java\\controller.java");
        templateConfig.setService("\\templates\\java\\service.java");
        templateConfig.setServiceImpl("\\templates\\java\\serviceImpl.java");
        templateConfig.setMapper("\\templates\\java\\mapper.java");
        templateConfig.setEntity("\\templates\\java\\entity.java");
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // MybatisPlus策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setEntityLombokModel(true);
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 继承父类实体,没有就不用设置
        strategy.setSuperEntityClass(BaseEntity.class);
        strategy.setRestControllerStyle(true);
        // strategy.setEntityTableFieldAnnotationEnable(true);
        strategy.setInclude(scanner("数据库表名(多个用,隔开)").split(","));
        if ("Y".equals(scanner("是/否[去除包名?](Y/N 输入字母必须大写)"))) {
            strategy.setTablePrefix(scanner("表前缀名称(例:表名[tlxx_base],输入[tlxx_])"));
        }
        // 逻辑删除字段
        strategy.setLogicDeleteFieldName(DataTableFields.deleted);
        // 开启驼峰连字符
        strategy.setControllerMappingHyphenStyle(false);
        // 在实体类中移除is前缀
        strategy.setEntityBooleanColumnRemoveIsPrefix(true);
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

    /**
     * 菜单SQL生成器
     *
     * @param jdbcUrl
     * @param driverName
     * @param userName
     * @param passWord
     */
    public static void menuGenerator(String jdbcUrl, String driverName, String userName, String passWord) {
        String simpleName = scanner("服务网关转发名称");
        String menuName = scanner("菜单中文简称");
        String moduleName = scanner("定义Controller文件@RequestMapping名称");
        String buttonName = scanner("按钮中文简称");
        Long parentMenuId = Long.parseLong(scanner("创建上级菜单:默认:0,(或指定上级菜单:输入父级菜单id)"));
        JDBCUtils jdbc = new JDBCUtils(jdbcUrl, driverName, userName, passWord);
        // 拼接添加菜单SQL
        StringBuffer menuStr = new StringBuffer();
        Long menuId = 0L;
        // 判断是否创建上级菜单并拼接菜单SQL
        if (parentMenuId == 0) {
            menuStr.append("insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, " +
                    "is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, " +
                    "update_time, remark) values ");
            menuStr.append("('").append(menuName).append("', '").append(parentMenuId).append("', '1', '")
                    .append(moduleName).append("', '").append(simpleName + "/" + moduleName + "/index")
                    .append("', 1, 0, 'C', '0', '0', ");
            menuStr.append("'").append(simpleName + ":" + moduleName + ":list").append("', '#', 'system'," +
                    " sysdate(), '', null, '").append(menuName + "菜单").append("');").append("\n");
            System.out.println(menuStr.toString());
            menuId = jdbc.saveSqlBackId(menuStr.toString());
        } else {
            // 不需要创建菜单,获取自定义父级id并赋值
            menuId = parentMenuId;
        }
        // 拼接菜单按钮SQL
        if (menuId > 0) {
            System.out.println("添加菜单成功[SUCCESS],菜单主键：" + menuId);
            menuStr.setLength(0);
            // 拼接查询按钮SQL
            menuStr.append("insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, " +
                    "is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, " +
                    "update_time, remark) values ");
            menuStr.append("('" + buttonName + "查询',").append(menuId)
                    .append(", '1', '#', '', 1, 0, 'F', '0', '0', '")
                    .append(simpleName + ":" + moduleName + ":query")
                    .append("', '#', 'system', sysdate(), '', null, '');")
                    .append("\n");
            // 拼接添加按钮SQL
            menuStr.append("insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, " +
                    "is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, " +
                    "update_time, remark) values ");
            menuStr.append("('" + buttonName + "新增',").append(menuId)
                    .append(", '2', '#', '', 1, 0, 'F', '0', '0', '")
                    .append(simpleName + ":" + moduleName + ":add")
                    .append("', '#', 'system', sysdate(), '', null, '');")
                    .append("\n");
            // 拼接修改按钮SQL
            menuStr.append("insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, " +
                    "is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, " +
                    "update_time, remark) values ");
            menuStr.append("('" + buttonName + "编辑',").append(menuId)
                    .append(", '3', '#', '', 1, 0, 'F', '0', '0', '")
                    .append(simpleName + ":" + moduleName + ":edit")
                    .append("', '#', 'system', sysdate(), '', null, '');")
                    .append("\n");
            // 拼接删除按钮SQL
            menuStr.append("insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, " +
                    "is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, " +
                    "update_time, remark) values ");
            menuStr.append("('" + buttonName + "删除',").append(menuId)
                    .append(", '4', '#', '', 1, 0, 'F', '0', '0', '")
                    .append(simpleName + ":" + moduleName + ":remove")
                    .append("', '#', 'system', sysdate(), '', null, '');")
                    .append("\n");
            System.out.println(menuStr.toString());
            if (jdbc.saveBatchSql(menuStr.toString())) {
                System.out.println("创建菜单/按钮成功[SUCCESS]");
            } else {
                System.out.println("创建菜单/按钮失败[ERROR]");
            }
        } else {
            System.out.println("创建菜单失败[ERROR]");
        }
        jdbc.closure();
    }

}
